/*  This file is part of the comicconquer project
    Copyright (C) 2010 Reach <reach334@gmail.com>

    ComicConquer is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This software is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public 
    License along with this software.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "GrayScaleDlg.h"

extern QDialog* g_mainDialog;

GrayScaleDlg::GrayScaleDlg(QImage* imgSrc, GrayScaleSet& grayScaleSet, QWidget* parent) : QDialog(parent)
{
    In_Log(__FUNCTION__, COCLASSNAME);
    resize(g_mainDialog->rect().width(), g_mainDialog->rect().height());

    CO_MOUSEATTATCH;

    // create color table
    for (int i = 0; i < 256; i++)
    {
        m_rgbTable.append(qRgb(i, i, i));
    }
    m_imgDst = imgSrc;
    m_imgDst->setColorTable(m_rgbTable);

    m_imgHistogram = new QImage(256, 100, QImage::Format_Indexed8);
    m_imgHistogram->setColorTable(m_rgbTable);

    // display rects
    m_rcImage = m_imgDst->rect().translated(((rect().width() - m_imgDst->rect().width())>>1), 0);
    m_rcSetting.setRect(0, 0, rect().width(), 206);
    m_rcBlack = QRect(0, 0, GS_BLOCK_W, GS_BLOCK_H);
    m_rcWhite = QRect(0, 0, GS_BLOCK_W, GS_BLOCK_H);
    m_rcMiddle = QRect(0, 0, GS_BLOCK_W, GS_BLOCK_H);

    // slide bar
    drawHistogram();
    m_iGrayBarY = m_imgHistogram->height() + (GS_BLOCK_H>>1);
    m_iGrayBarLen = rect().width() - 100;
    m_iGrayBarStart = 50;
    m_iGrayBarEnd = 50 + m_iGrayBarLen;
    m_rcGrayBar.setRect(m_iGrayBarStart - (GS_BLOCK_W>>1), m_imgHistogram->height(),
                        m_iGrayBarLen + GS_BLOCK_W, GS_BLOCK_H);

    m_scaleBlack = grayScaleSet.black;
    m_scaleWhite = grayScaleSet.white;
    m_rcBlack.moveCenter(QPoint(m_iGrayBarStart + m_iGrayBarLen*m_scaleBlack/255, m_iGrayBarY));
    m_rcWhite.moveCenter(QPoint(m_iGrayBarStart + m_iGrayBarLen*m_scaleWhite/255, m_iGrayBarY));

    setMiddleBlock(grayScaleSet.middle);

    setGrayTable(m_rgbTable, m_scaleBlack, m_scaleMiddle, m_scaleWhite);
    m_imgDst->setColorTable(m_rgbTable);

    m_blockSelect = BlockMiddle;

    // ui
    QWidget* w = new QWidget(this);
    w->move((rect().width()>>1) - 100, m_imgHistogram->height() + GS_BLOCK_H + GS_TEXT_H);

    m_hlayout = new QHBoxLayout(w);
    m_bOK = new ui::OnyxPushButton("确定", this);
    m_hlayout->addWidget(m_bOK);
    m_bReset = new ui::OnyxPushButton("重置", this);
    m_hlayout->addWidget(m_bReset);
    m_bCancel = new ui::OnyxPushButton("取消", this);
    m_hlayout->addWidget(m_bCancel);

    connect(m_bOK, SIGNAL(clicked()), this, SLOT(accept()));
    connect(m_bCancel, SIGNAL(clicked()), this, SLOT(reject()));
    connect(m_bReset, SIGNAL(clicked()), this, SLOT(reset()));

#ifdef QT_ARCH_ARM
    showFullScreen();
#endif

    Out_Log(__FUNCTION__, COCLASSNAME);
}

GrayScaleDlg::~GrayScaleDlg()
{

}

void GrayScaleDlg::paintEvent(QPaintEvent* event)
{
    event->type();
    Write_Log("GrayScale paint\n");
    QPainter painter(this);
    painter.drawImage(m_rcImage.translated(0, m_rcSetting.height()), *m_imgDst);
    painter.fillRect(m_rcSetting, Qt::white);
    painter.drawImage(QRect(50, 0, rect().width() - 100, m_imgHistogram->height()), *m_imgHistogram);
    painter.drawLine(m_iGrayBarStart, m_iGrayBarY, m_iGrayBarEnd, m_iGrayBarY);
    painter.drawRect(m_rcBlack);
    painter.drawRect(m_rcWhite);
    painter.drawRect(m_rcMiddle);
    painter.fillRect(m_rcBlack, QColor(Qt::white));
    painter.fillRect(m_rcWhite, QColor(Qt::white));
    painter.fillRect(m_rcMiddle, QColor(Qt::white));

    // draw texts
    QRect rcText(0, m_rcBlack.bottom() + 5, GS_BLOCK_W, GS_TEXT_H);
    rcText.moveLeft(m_rcBlack.left());
    painter.drawText(rcText, Qt::AlignCenter, QString::number(m_scaleBlack));
    rcText.moveLeft(m_rcMiddle.left());
    painter.drawText(rcText, Qt::AlignCenter, QString::number(m_scaleMiddle, 'f', 2));
    rcText.moveLeft(m_rcWhite.left());
    painter.drawText(rcText, Qt::AlignCenter, QString::number(m_scaleWhite));

    switch (m_blockSelect)
    {
    case BlockBlack:
        {
            painter.fillRect(m_rcBlack, QColor(Qt::black));
        }
        break;
    case BlockWhite:
        {
            painter.fillRect(m_rcWhite, QColor(Qt::black));
        }
        break;
    case BlockMiddle:
        {
            painter.fillRect(m_rcMiddle, QColor(Qt::black));
        }
        break;
    default:
        {

        }
        break;
    }   // switch (m_blockSelect)
}

bool GrayScaleDlg::event(QEvent* e)
{
    bool ret = QDialog::event(e);

    switch (e->type())
    {
    case QEvent::UpdateRequest:
        {
            refreshScreen();
            return true;
        }
        break;
    default:
        {

        }
        break;
    }
    return ret;
}

void GrayScaleDlg::drawHistogram()
{
    int iHistogram[256];
    memset(iHistogram, 0, sizeof(iHistogram));
    int maxNumBlack = 0;    // max pixels number between 0 and 200
    uchar* data = m_imgDst->bits();
    int bytesPerLineDst = m_imgDst->bytesPerLine();
    int width = m_imgDst->width();
    int height = m_imgDst->height();

    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            ++iHistogram[*data];
            if (*data < 200 && iHistogram[*data] > maxNumBlack) maxNumBlack = iHistogram[*data];
            ++data;
        }
        data += bytesPerLineDst - width;
    }

    m_imgHistogram->fill(255);
    height = m_imgHistogram->height();
    float rate = (float)height / maxNumBlack;

    for (int x = 0; x < 256; x++)
    {
        for (int y = height-1; y >= height - iHistogram[x]*rate && y >= 0; y--)
        {
            m_imgHistogram->setPixel(x, y, 0);
        }
    }
}

GrayScaleSet GrayScaleDlg::setGrayScale(QImage* imgSrc, GrayScaleSet& grayScaleSet, QWidget* parent)
{
    GrayScaleDlg dlg(imgSrc, grayScaleSet, parent);
    GrayScaleSet gs;
    gs.black = -1;
    if (dlg.exec() == Accepted)
    {
        gs.black = dlg.m_scaleBlack;
        gs.middle = dlg.m_scaleMiddle;
        gs.white = dlg.m_scaleWhite;
    }

    return gs;
}

void GrayScaleDlg::mouseCtlEvent(GestureEvent* event)
{
    switch (event->gesture)
    {
    case DragNone:
        {
            if (!m_rcGrayBar.contains(event->clickPos))
            {
                return; 
            }

            if (m_rcBlack.contains(event->clickPos))
            {
                m_blockSelect = BlockBlack;
            }
            else if (m_rcWhite.contains(event->clickPos))
            {
                m_blockSelect = BlockWhite;
            }
            else if (m_rcMiddle.contains(event->clickPos))
            {
                m_blockSelect = BlockMiddle;
            }

            int clickX = event->clickPos.x();
            switch (m_blockSelect)
            {
            case BlockBlack:
                {
                    clickX = CO_MAX(clickX, m_iGrayBarStart);
                    clickX = CO_MIN(clickX, m_rcMiddle.center().x() - GS_BLOCK_W - 1);
                    m_rcBlack.moveCenter(QPoint(clickX, m_iGrayBarY));
                    m_scaleBlack = (float)(clickX - m_iGrayBarStart)/m_iGrayBarLen * 255;
                    setMiddleBlock(m_scaleMiddle);
                }
                break;
            case BlockWhite:
                {
                    clickX = CO_MIN(clickX, m_iGrayBarEnd);
                    clickX = CO_MAX(clickX, m_rcMiddle.center().x() + GS_BLOCK_W + 1);
                    m_rcWhite.moveCenter(QPoint(clickX, m_iGrayBarY));
                    m_scaleWhite = (float)(clickX - m_iGrayBarStart)/m_iGrayBarLen * 255;
                    setMiddleBlock(m_scaleMiddle);
                }
                break;
            case BlockMiddle:
                {
                    clickX = CO_MAX(clickX, m_rcBlack.center().x() + GS_BLOCK_W +1);
                    clickX = CO_MIN(clickX, m_rcWhite.center().x() - GS_BLOCK_W - 1);
                    setMiddleBlock(clickX);
                }
                break;
            default:
                {

                }
                break;
            }   // switch (m_blockSelect)
            setGrayTable(m_rgbTable, m_scaleBlack, m_scaleMiddle, m_scaleWhite);
            m_imgDst->setColorTable(m_rgbTable);
            update();
        }   // case DragNone
        break;
    case DragUp<<3 | DragRight:
        {
        }
        break;

    // Any drag
    default:
        {
            if (!m_rcGrayBar.contains(event->clickPos))
            {
                return; 
            }

            if (m_rcBlack.contains(event->clickPos))
            {
                m_blockSelect = BlockBlack;
            }
            else if (m_rcWhite.contains(event->clickPos))
            {
                m_blockSelect = BlockWhite;
            }
            else if (m_rcMiddle.contains(event->clickPos))
            {
                m_blockSelect = BlockMiddle;
            }

            int clickX = event->clickPos.x() + event->offset.x();
            switch (m_blockSelect)
            {
            case BlockBlack:
                {
                    clickX = CO_MAX(clickX, m_iGrayBarStart);
                    clickX = CO_MIN(clickX, m_rcMiddle.center().x() - GS_BLOCK_W - 1);
                    m_rcBlack.moveCenter(QPoint(clickX, m_iGrayBarY));
                    m_scaleBlack = (float)(clickX - m_iGrayBarStart)/m_iGrayBarLen * 255;
                    setMiddleBlock(m_scaleMiddle);
                }
                break;
            case BlockWhite:
                {
                    clickX = CO_MIN(clickX, m_iGrayBarEnd);
                    clickX = CO_MAX(clickX, m_rcMiddle.center().x() + GS_BLOCK_W + 1);
                    m_rcWhite.moveCenter(QPoint(clickX, m_iGrayBarY));
                    m_scaleWhite = (float)(clickX - m_iGrayBarStart)/m_iGrayBarLen * 255;
                    setMiddleBlock(m_scaleMiddle);
                }
                break;
            case BlockMiddle:
                {
                    clickX = CO_MAX(clickX, m_rcBlack.center().x() + GS_BLOCK_W +1);
                    clickX = CO_MIN(clickX, m_rcWhite.center().x() - GS_BLOCK_W - 1);
                    setMiddleBlock(clickX);
                }
                break;
            default:
                {

                }
                break;
            }   // switch (m_blockSelect)
            setGrayTable(m_rgbTable, m_scaleBlack, m_scaleMiddle, m_scaleWhite);
            m_imgDst->setColorTable(m_rgbTable);
            update();
        }   // default
        break;
    }
}

void GrayScaleDlg::setGrayTable(QVector<QRgb>& rgbTable, const int iBlack, const float fMiddle, const int iWhite)
{
    rgbTable.clear();
    uchar gray;
    for (int i = 0; i < 256; i++)
    {
        if (i <= iBlack)
        {
            gray = 0; 
        }
        else if (i >= iWhite)
        {
            gray = 255; 
        }
        else
        {
            int disIn = iWhite - iBlack;
            gray = std::pow((float)(i - iBlack) / disIn, fMiddle) * 255;
        }
        rgbTable.append(qRgb(gray, gray, gray));
    }
}

void GrayScaleDlg::setMiddleBlock(float scaleMiddle)
{
    m_scaleMiddle = scaleMiddle;

    float cof = (std::log10(scaleMiddle) + 1) / 2;
    int xBlack = m_rcBlack.center().x();
    int xWhite = m_rcWhite.center().x();
    int dis = xWhite - xBlack;
    m_rcMiddle.moveCenter(QPoint(xBlack + dis * cof, m_iGrayBarY));
}

void GrayScaleDlg::setMiddleBlock(int xPos)
{
    m_rcMiddle.moveCenter(QPoint(xPos, m_iGrayBarY));

    int xBlack = m_rcBlack.center().x();
    int xWhite = m_rcWhite.center().x();
    int dis = xWhite - xBlack;
    float cof = (float)(xPos - xBlack) / dis;
    m_scaleMiddle = std::pow(10, cof*2 - 1);
}

void GrayScaleDlg::reset()
{
    m_scaleBlack = 0;
    m_scaleWhite = 255;
    m_rcBlack.moveCenter(QPoint(m_iGrayBarStart + m_iGrayBarLen*m_scaleBlack/255, m_iGrayBarY));
    m_rcWhite.moveCenter(QPoint(m_iGrayBarStart + m_iGrayBarLen*m_scaleWhite/255, m_iGrayBarY));

    setMiddleBlock(1.0f);

    setGrayTable(m_rgbTable, m_scaleBlack, m_scaleMiddle, m_scaleWhite);
    m_imgDst->setColorTable(m_rgbTable);

    update();
}
